﻿using System;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using NTmdb;
using NTmdb.Net;

namespace CommandLineSample
{
    public class Program
    {
        public const String SessionId = "8958bf504776aa49f670af8b22c18a26ee05a580";
        private const String ApiKey = "ba9a4d5a02b4438300a712d31316a3ff";

        public static void Main( String[] args )
        {
            //Task.WaitAll( MainAsync( args ) );
            //Task.WaitAll( EasyMainAsync( args ) );
            //Task.WaitAll( DownloadImages() );
            Task.WaitAll(SampeGettingStarted());
            Console.WriteLine( "Press Escape to exit." );
            while ( Console.ReadKey().Key != ConsoleKey.Escape )
                Console.WriteLine( "." );
        }

        public static async Task MainAsync( String[] args )
        {
            var api = new ApiWrapper
                {
                    //Platform specific type (networking)
                    ApiClient = new ApiClientNet()
                };
            var config = new ApiConfiguration();
            config.RestoreDefaultValues( ApiKey );
            api.ApiConfiguration = config;

            var searchResult = await api.SearchMovieAsync( "The lord of the rings" ).UnwrapOrThrow();
            Console.WriteLine( "Found {0} movies", searchResult.TotalResults );

            foreach ( var moviePreview in searchResult.Movies )
            {
                var movie = await api.GetMovieAsync( moviePreview.Id, TmdbMovieMethod.All ).UnwrapOrThrow();

                Console.WriteLine( "Id:\t\t\t\t{0}(IMDb Id: {1})", movie.Id, movie.ImdbId );
                Console.WriteLine();
                Console.WriteLine( "Title:\t\t\t\t{0}", movie.Title );
                Console.WriteLine();
                Console.WriteLine( "Original Title:\t\t\t{0}", movie.OriginalTitle );
                Console.WriteLine();
                Console.WriteLine( "Release Date:\t\t\t{0}", movie.GetReleaseDate( api.ApiConfiguration.JsonDatePattern ) );
                Console.WriteLine();
                Console.WriteLine( "Belongs to Collection:\t\t{0}", movie.BelongsToCollection != null ? movie.BelongsToCollection.Name : "-" );
                Console.WriteLine();
                Console.WriteLine( "Is Adult:\t\t\t{0}", movie.IsAdult );
                Console.WriteLine();
                Console.WriteLine( "Vote Average:\t\t\t{0}", movie.VoteAverage );
                Console.WriteLine();
                Console.WriteLine( "Vote Count:\t\t\t{0}", movie.VoteCount );
                Console.WriteLine();
                Console.WriteLine( "Popularity:\t\t\t{0}", movie.Popularity );
                Console.WriteLine();
                Console.WriteLine( "Runtime:\t\t\t{0}", movie.Runtime );
                Console.WriteLine();
                Console.WriteLine( "Homepage:\t\t\t{0}", movie.Homepage );
                Console.WriteLine();
                Console.WriteLine( "Tagline:\t\t\t{0}", movie.Tagline );
                Console.WriteLine();
                Console.WriteLine( "Budget:\t\t\t\t{0}", movie.Budget );
                Console.WriteLine();
                Console.WriteLine( "Revenue:\t\t\t{0}", movie.Revenue );
                Console.WriteLine();
                Console.WriteLine( "Overview:\t\t\t{0}", movie.Overview );
                Console.WriteLine();
                Console.WriteLine( "Poster Path:\t\t\t{0}", movie.PosterPath );
                Console.WriteLine();
                Console.WriteLine( "Backdrop Path:\t\t\t{0}", movie.BackdropPath );
                Console.WriteLine();

                Console.WriteLine( "Genres:\t\t\t\t{0}",
                                   movie.Genres != null && movie.Genres.Any()
                                       ? movie.Genres.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Production Countries:\t\t{0}",
                                   movie.ProductionCountries != null && movie.ProductionCountries.Any()
                                       ? movie.ProductionCountries.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Production Companies:\t\t{0}",
                                   movie.ProductionCompanies != null && movie.ProductionCompanies.Any()
                                       ? movie.ProductionCompanies.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Spoken Languages:\t\t{0}",
                                   movie.SpokenLanguages != null && movie.SpokenLanguages.Any()
                                       ? movie.SpokenLanguages.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Alternative Titles:\t\t{0}",
                                   movie.AlternativeTitles != null && movie.AlternativeTitles.Titles.Any()
                                       ? movie.AlternativeTitles.Titles.Select( x => x.Title ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Keywords:\t\t\t{0}",
                                   movie.Keywords != null && movie.Keywords.Keywords.Any()
                                       ? movie.Keywords.Keywords.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Releases:\t\t\t{0}",
                                   movie.Releases != null && movie.Releases.Releases.Any()
                                       ? movie.Releases.Releases.Select( x => x.ReleaseDate ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Translations:\t\t\t{0}",
                                   movie.Translations != null && movie.Translations.Translations.Any()
                                       ? movie.Translations.Translations.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "SimilarMovies:\t\t\t{0}",
                                   movie.SimilarMovies != null && movie.SimilarMovies.Movies.Any()
                                       ? movie.SimilarMovies.Movies.Select( x => x.Title ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Trailers:\t\t\t{0}",
                                   movie.Trailers != null && movie.Trailers.Youtube.Any()
                                       ? movie.Trailers.Youtube.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Trailers:\t\t\t{0}",
                                   movie.Lists != null && movie.Lists.MovieLists.Any()
                                       ? movie.Lists.MovieLists.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "MovieReviews:\t\t\t{0}",
                                   movie.MovieReviews != null && movie.MovieReviews.Reviews.Any()
                                       ? movie.MovieReviews.Reviews.Select( x => x.Content )
                                              .Aggregate( ( a, b ) => String.Concat( a, Environment.NewLine, Environment.NewLine, b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Crew:\t\t\t\t{0}",
                                   movie.Staff != null && movie.Staff.Crew.Any()
                                       ? movie.Staff.Crew.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );
                Console.WriteLine();
                Console.WriteLine( "Cast:\t\t\t\t{0}",
                                   movie.Staff != null && movie.Staff.Cast.Any()
                                       ? movie.Staff.Cast.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                       : "-" );

                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine( "Press any key to display the information of the next movie." );
                Console.ReadKey();
                Console.Clear();
            }
        }

        public static async Task EasyMainAsync( String[] args )
        {
            var apiConfiguration = new ApiConfiguration();
            apiConfiguration.RestoreDefaultValues( ApiKey );
            var tmdb = new TmdbAbstraction( new ApiWrapper
                {
                    ApiConfiguration = apiConfiguration,
                    ApiClient = new ApiClientNet()
                } );

            try
            {
                var searchResult = await tmdb.SearchMovieAsync( "Matrix" );
                Console.WriteLine( "Found {0} movies", searchResult.TotalResults );

                foreach ( var moviePreview in searchResult.Movies )
                {
                    var movie = await tmdb.GetMovieAsync( moviePreview.Id, TmdbMovieMethod.All );

                    Console.WriteLine( "Id:\t\t\t\t{0}(IMDb Id: {1})", movie.Id, movie.ImdbId );
                    Console.WriteLine();
                    Console.WriteLine( "Title:\t\t\t\t{0}", movie.Title );
                    Console.WriteLine();
                    Console.WriteLine( "Original Title:\t\t\t{0}", movie.OriginalTitle );
                    Console.WriteLine();
                    Console.WriteLine( "Release Date:\t\t\t{0}", movie.GetReleaseDate( tmdb.ApiWrapper.ApiConfiguration.JsonDatePattern ) );
                    Console.WriteLine();
                    Console.WriteLine( "Belongs to Collection:\t\t{0}", movie.BelongsToCollection != null ? movie.BelongsToCollection.Name : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Is Adult:\t\t\t{0}", movie.IsAdult );
                    Console.WriteLine();
                    Console.WriteLine( "Vote Average:\t\t\t{0}", movie.VoteAverage );
                    Console.WriteLine();
                    Console.WriteLine( "Vote Count:\t\t\t{0}", movie.VoteCount );
                    Console.WriteLine();
                    Console.WriteLine( "Popularity:\t\t\t{0}", movie.Popularity );
                    Console.WriteLine();
                    Console.WriteLine( "Runtime:\t\t\t{0}", movie.Runtime );
                    Console.WriteLine();
                    Console.WriteLine( "Homepage:\t\t\t{0}", movie.Homepage );
                    Console.WriteLine();
                    Console.WriteLine( "Tagline:\t\t\t{0}", movie.Tagline );
                    Console.WriteLine();
                    Console.WriteLine( "Budget:\t\t\t\t{0}", movie.Budget );
                    Console.WriteLine();
                    Console.WriteLine( "Revenue:\t\t\t{0}", movie.Revenue );
                    Console.WriteLine();
                    Console.WriteLine( "Overview:\t\t\t{0}", movie.Overview );
                    Console.WriteLine();
                    Console.WriteLine( "Poster Path:\t\t\t{0}", movie.PosterPath );
                    Console.WriteLine();
                    Console.WriteLine( "Backdrop Path:\t\t\t{0}", movie.BackdropPath );
                    Console.WriteLine();

                    Console.WriteLine( "Genres:\t\t\t\t{0}",
                                       movie.Genres != null && movie.Genres.Any()
                                           ? movie.Genres.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Production Countries:\t\t{0}",
                                       movie.ProductionCountries != null && movie.ProductionCountries.Any()
                                           ? movie.ProductionCountries.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Production Companies:\t\t{0}",
                                       movie.ProductionCompanies != null && movie.ProductionCompanies.Any()
                                           ? movie.ProductionCompanies.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Spoken Languages:\t\t{0}",
                                       movie.SpokenLanguages != null && movie.SpokenLanguages.Any()
                                           ? movie.SpokenLanguages.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Alternative Titles:\t\t{0}",
                                       movie.AlternativeTitles != null && movie.AlternativeTitles.Titles.Any()
                                           ? movie.AlternativeTitles.Titles.Select( x => x.Title )
                                                  .Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Keywords:\t\t\t{0}",
                                       movie.Keywords != null && movie.Keywords.Keywords.Any()
                                           ? movie.Keywords.Keywords.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Releases:\t\t\t{0}",
                                       movie.Releases != null && movie.Releases.Releases.Any()
                                           ? movie.Releases.Releases.Select( x => x.ReleaseDate ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Translations:\t\t\t{0}",
                                       movie.Translations != null && movie.Translations.Translations.Any()
                                           ? movie.Translations.Translations.Select( x => x.Name )
                                                  .Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "SimilarMovies:\t\t\t{0}",
                                       movie.SimilarMovies != null && movie.SimilarMovies.Movies.Any()
                                           ? movie.SimilarMovies.Movies.Select( x => x.Title ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Trailers:\t\t\t{0}",
                                       movie.Trailers != null && movie.Trailers.Youtube.Any()
                                           ? movie.Trailers.Youtube.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Trailers:\t\t\t{0}",
                                       movie.Lists != null && movie.Lists.MovieLists.Any()
                                           ? movie.Lists.MovieLists.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "MovieReviews:\t\t\t{0}",
                                       movie.MovieReviews != null && movie.MovieReviews.Reviews.Any()
                                           ? movie.MovieReviews.Reviews.Select( x => x.Content )
                                                  .Aggregate( ( a, b ) => String.Concat( a, Environment.NewLine, Environment.NewLine, b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Crew:\t\t\t\t{0}",
                                       movie.Staff != null && movie.Staff.Crew.Any()
                                           ? movie.Staff.Crew.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );
                    Console.WriteLine();
                    Console.WriteLine( "Cast:\t\t\t\t{0}",
                                       movie.Staff != null && movie.Staff.Cast.Any()
                                           ? movie.Staff.Cast.Select( x => x.Name ).Aggregate( ( a, b ) => String.Concat( a, ", ", b ) )
                                           : "-" );

                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine( "Press any key to display the information of the next movie." );
                    Console.ReadKey();
                    Console.Clear();
                }
            }
            catch ( TmdbException ex )
            {
                Console.WriteLine( ex.InnerException.Message );
                Console.WriteLine( ex.ApiError );
            }
        }

        public static async Task DownloadImages()
        {
            var apiConfiguration = new ApiConfiguration();
            apiConfiguration.RestoreDefaultValues( ApiKey );
            var tmdb = new TmdbAbstraction( new ApiWrapper
                {
                    ApiConfiguration = apiConfiguration,
                    ApiClient = new ApiClientNet()
                } );

            var config = await tmdb.GetConfigurationAsync();
            var client = new ApiClientNet();

            ThreadPool.SetMaxThreads( 1000, 1000 );
            ServicePointManager.DefaultConnectionLimit = 1000;
            Parallel.For( 0, 500000, new ParallelOptions
                {
                    MaxDegreeOfParallelism = 200
                }, async ( Int32 i ) =>
                    {
                        try
                        {
                            var movie = await tmdb.GetMovieAsync( i, TmdbMovieMethod.Images );
                            if ( movie.Images == null )
                                return;

                            if ( movie.Images.Posters.Any() )
                                Parallel.ForEach( movie.Images.Posters, async image =>
                                    {
                                        var imageUrl = String.Format( "{0}/{1}/{2}", config.ImageConfiguration.BaseUrl, "original", image.FilePath );
                                        var result =
                                            await
                                            client.DownloadFileAsync( imageUrl,
                                                                      String.Format( @"C:\Temp\Img\{0}_Poster_{1}", i,
                                                                                     image.FilePath.Replace( '/', '_' ) ) );
                                        var res = result.FilePath;
                                        Console.WriteLine( "Saved file: {0}", result.FilePath );
                                    } );
                            if ( movie.Images.Backdrops.Any() )
                                Parallel.ForEach( movie.Images.Backdrops, async image =>
                                    {
                                        var imageUrl = String.Format( "{0}/{1}/{2}", config.ImageConfiguration.BaseUrl, "original", image.FilePath );
                                        var result =
                                            await
                                            client.DownloadFileAsync( imageUrl,
                                                                      String.Format( @"C:\Temp\Img\{0}_Backdrop_{1}", i,
                                                                                     image.FilePath.Replace( '/', '_' ) ) );
                                        var res = result.FilePath;
                                        Console.WriteLine( "Saved file: {0}", result.FilePath );
                                    } );
                        }
                        catch ( Exception ex )
                        {
                            Console.WriteLine( "\t\t\t{0} Failed - {1}", i, ex.InnerException.Message );
                        }
                    } );
        }

        public static async Task SampeGettingStarted()
        {
            //Create a API configuration.
            var config = new ApiConfiguration();
            //Set the API key.
            config.RestoreDefaultValues(ApiKey);

            //Creating a ApiWrapper.
            var api = new ApiWrapper
                {
                    //Platform specific type (networking).
                    ApiClient = new ApiClientNet(),
                    ApiConfiguration = config
                };

            var review = await api.GetMovieAsync( 68726, TmdbMovieMethod.All ).UnwrapOrThrow();
            string s = review.Overview;
        }

        public static async Task SampeGettingStarted1()
        {
            //Create a API configuration.
            var config = new ApiConfiguration();
            //Set the API key.
            config.RestoreDefaultValues( "Insert you API key here" );

            //Creating a ApiWrapper.
            var api = new ApiWrapper
            {
                //Platform specific type (networking).
                ApiClient = new ApiClientNet(),
                ApiConfiguration = config
            };
            //Create a instance of the abstraction class
            var abstraction = new TmdbAbstraction( api );

        }
    }
}